package api

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"reflect"
	"strings"
)

import (
	"github.com/guangzhou-meta/go-lib/tools"
)

import (
	"gitee.com/changeden/jd-union-go-sdk/common"
	"gitee.com/changeden/jd-union-go-sdk/pkg"
)

type apiBody struct {
	method      string  `json:"method"`
	appKey      string  `json:"app_key"`
	accessToken *string `json:"access_token"`
	timestamp   *string `json:"timestamp"`
	format      string  `json:"format"`
	v           string  `json:"v"`
	signMethod  string  `json:"sign_method"`
	sign        string  `json:"sign"`
	paramJson   string  `json:"360buy_param_json"`

	config  *apiConfig
	_params pkg.ApiRequestBody
}

func NewApiBody(config *apiConfig) *apiBody {
	return &apiBody{
		format:     common.DefaultFormat,
		v:          common.DefaultVersion,
		signMethod: common.DefaultSignMethod,
		appKey:     config.appKey,
		config:     config,
	}
}

func (b *apiBody) SetMethod(method string) *apiBody {
	b.method = method
	return b
}

func (b *apiBody) SetTimestamp(timestamp *string) *apiBody {
	b.timestamp = timestamp
	return b
}

func (b *apiBody) SetAccessToken(accessToken *string) *apiBody {
	b.accessToken = accessToken
	return b
}

func (b *apiBody) SetParamJson(params pkg.ApiRequestBody) *apiBody {
	b._params = params
	return b
}

func (b *apiBody) parseParamJson() error {
	if b._params == nil {
		b.paramJson = common.DefaultParamJson
		return nil
	}
	j, err := json.Marshal(map[string]pkg.ApiRequestBody{
		b._params.GetDTOName(): b._params,
	})
	if err == nil {
		b.paramJson = string(j)
		return nil
	}
	return err
}

func request(b *apiBody) (resp *http.Response, err error) {
	var req *http.Request
	req, err = http.NewRequest(common.RequestMethod, b.config.serverAddress, nil)
	if err != nil {
		log.Println(err)
		return
	}
	query := req.URL.Query()

	e0 := tools.UnpackType(reflect.TypeOf(b))
	e1 := tools.UnpackValue(reflect.ValueOf(b))
	for i := 0; i < e0.NumField(); i++ {
		f0 := e0.Field(i)
		t := strings.TrimSpace(f0.Tag.Get("json"))
		if len(t) == 0 {
			continue
		}
		f1 := tools.UnpackValue(e1.Field(i))
		query.Add(t, f1.String())
	}

	req.URL.RawQuery = query.Encode()

	client := &http.Client{}
	resp, err = client.Do(req)
	return resp, err
}

func (b *apiBody) Request(wrapKey string) (*pkg.ApiResponseDTO, error) {
	var err error
	_, err = Sign(b, b.config.appSecret)
	if err != nil {
		log.Println(err)
		return nil, err
	}
	var resp *http.Response
	resp, err = request(b)
	if err != nil {
		log.Println(err)
		return nil, err
	}
	log.Println("请求链接: ", resp.Request.URL)
	var bodyByte []byte
	bodyByte, err = ioutil.ReadAll(resp.Body)
	var r map[string]interface{}
	err = json.Unmarshal(bodyByte, &r)
	if err != nil {
		log.Println(err)
		return nil, err
	}
	var resBody interface{}
	var existed bool
	if resBody, existed = r[common.ApiResponseErrorMsgKey]; existed {
		return genErrorResponse(resBody.(map[string]interface{})), nil
	}
	wrapKey = strings.TrimSpace(wrapKey)
	if len(wrapKey) == 0 {
		resBody = r
	}
	if resBody, existed = r[wrapKey]; !existed { // 通过特殊的键获取响应体
		resBody = common.DefaultParamJson
	}
	return genResultResponse(resBody.(map[string]interface{})), nil
}

func genResultResponse(m map[string]interface{}) *pkg.ApiResponseDTO {
	var code string
	if c, ok := m[common.ApiResponseCodeKey]; ok {
		code = c.(string)
	}
	var query string
	if queryResult, ok := m[common.ApiResponseResultKey]; ok {
		query = queryResult.(string)
	}
	return &pkg.ApiResponseDTO{
		Result: &pkg.ResultResponse{
			Code:        code,
			QueryResult: query,
		},
	}
}

func genErrorResponse(m map[string]interface{}) *pkg.ApiResponseDTO {
	var code string
	if c, ok := m[common.ApiResponseCodeKey]; ok {
		code = c.(string)
	}
	var zh string
	if z, ok := m[common.ApiResponseErrorZHDescKey]; ok {
		zh = z.(string)
	}
	var en string
	if e, ok := m[common.ApiResponseErrorENDescKey]; ok {
		en = e.(string)
	}
	return &pkg.ApiResponseDTO{
		Error: &pkg.ErrorResponse{
			Code:   code,
			ZHDesc: zh,
			ENDesc: en,
		},
	}
}
